iT邦幫忙

2025 iThome 鐵人賽

DAY 2
0
Modern Web

React TDD 實戰:用 Vitest 打造可靠的前端應用系列 第 2

Day 02 - 認識斷言(Assertions) 🚀

  • 分享至 

  • xImage
  •  

今天要做什麼?

昨天我們成功建立了測試環境並寫下第一個測試,今天要深入了解測試的核心 —「斷言(Assertions)」。

想像一下,你正在開發一個使用者註冊功能。產品經理說:「我們需要驗證使用者輸入的 email 格式、密碼強度、年齡範圍...」你心想:「這麼多驗證規則,怎麼確保每一個都正確運作?」

答案就是斷言!斷言是測試的核心,它告訴我們「期望」和「實際」結果是否相符。今天我們要學會使用各種斷言方法,讓測試更精準、更具表達力。

學習目標

今天結束後,你將學會:

  • 理解斷言的作用和重要性
  • 掌握 Vitest 的各種斷言語法
  • 學會選擇適當的斷言方法
  • 理解斷言失敗時的調試技巧
  • 實踐常見的驗證場景測試

TDD 學習地圖

第一階段:打好基礎(Day 1-10)
├── Day 01 - 環境設置與第一個測試
├── Day 02 - 認識斷言(Assertions) ★ 今天在這裡
├── ...
└── (更多精彩內容待續)

什麼是斷言? 🧮

斷言就像品質檢驗員,負責檢查產品是否符合規格。在測試中,斷言:

  • 驗證結果:檢查函數回傳值是否正確
  • 表達期望:清楚說明我們期望的行為
  • 提供回饋:當測試失敗時給出明確的錯誤訊息

基本斷言語法 📝

昨天我們已經使用了最基本的 toBe 斷言:

建立 tests/day02/basic-assertions.test.ts

import { describe, it, expect } from 'vitest'

describe('basic assertions', () => {
  it('comparesEquality', () => {
    const result = 2 + 3
    expect(result).toBe(5)
  })
  
  it('differencesBetweenToBeAndToEqual', () => {
    const obj1 = { name: 'Alice' }
    const obj2 = { name: 'Alice' }
    
    // toBe 檢查是否為同一個物件(參考相等)
    expect(obj1).not.toBe(obj2)
    
    // toEqual 檢查內容是否相同(值相等)
    expect(obj1).toEqual(obj2)
  })
})

常用斷言方法 🔧

更新 tests/day02/basic-assertions.test.ts

import { describe, it, expect } from 'vitest'

describe('common assertion methods', () => {
  it('strictEquality', () => {
    expect(5).toBe(5)
    expect('hello').toBe('hello')
  })

  it('deepComparison', () => {
    const user = { name: 'Bob', age: 30 }
    expect(user).toEqual({ name: 'Bob', age: 30 })
  })

  it('checksNullAndUndefined', () => {
    const nullValue = null
    const undefinedValue = undefined
    
    expect(nullValue).toBeNull()
    expect(undefinedValue).toBeUndefined()
  })

  it('truthyAndFalsyChecks', () => {
    expect(true).toBeTruthy()
    expect('').toBeFalsy()
    expect(0).toBeFalsy()
    expect(1).toBeTruthy()
  })

  it('checksDataTypes', () => {
    expect(42).toBeTypeOf('number')
    expect('hello').toBeTypeOf('string')
  })
})

數值比較斷言 🔢

數值比較在驗證功能中經常用到:

建立 tests/day02/number-assertions.test.ts

import { describe, it, expect } from 'vitest'

describe('number comparison assertions', () => {
  it('greaterAndLessThan', () => {
    expect(10).toBeGreaterThan(5)
    expect(5).toBeLessThan(10)
  })

  it('greaterAndLessOrEqual', () => {
    expect(10).toBeGreaterThanOrEqual(10)
    expect(5).toBeLessThanOrEqual(5)
  })

  it('floatApproximation', () => {
    expect(0.1 + 0.2).toBeCloseTo(0.3)
  })
})

字串相關斷言 📖

字串驗證在表單處理中非常重要:

建立 tests/day02/string-assertions.test.ts

import { describe, it, expect } from 'vitest'

describe('string related assertions', () => {
  it('containsSubstring', () => {
    expect('Hello World').toContain('World')
    expect('user@example.com').toContain('@')
  })

  it('regexMatching', () => {
    expect('hello123').toMatch(/^hello\d+$/)
    expect('test@email.com').toMatch(/\w+@\w+\.\w+/)
  })

  it('checksLength', () => {
    expect('hello').toHaveLength(5)
    expect('').toHaveLength(0)
  })
})

陣列和物件斷言 📦

處理複雜資料結構時需要更強大的斷言:

建立 tests/day02/collection-assertions.test.ts

import { describe, it, expect } from 'vitest'

describe('collection assertions', () => {
  it('arrayContains', () => {
    const fruits = ['apple', 'banana', 'orange']
    expect(fruits).toContain('banana')
    expect(fruits).toHaveLength(3)
  })

  it('objectProperties', () => {
    const user = {
      id: 1,
      name: 'Alice',
      email: 'alice@example.com'
    }
    
    expect(user).toHaveProperty('name')
    expect(user).toHaveProperty('email', 'alice@example.com')
    expect(user).not.toHaveProperty('password')
  })

  it('objectMatching', () => {
    const product = {
      id: 1,
      name: 'iPhone',
      price: 999
    }
    
    // 部分匹配
    expect(product).toMatchObject({
      name: 'iPhone',
      price: 999
    })
  })
})

實作練習:驗證器函數 🎯

讓我們實作一個驗證器模組來練習各種斷言:

建立 tests/day02/validator.test.ts

import { describe, it, expect } from 'vitest'
import { isValidEmail, isStrongPassword, isValidAge } from '../../src/validator'

describe('validator function tests', () => {
  it('validatesEmail', () => {
    expect(isValidEmail('user@example.com')).toBe(true)
    expect(isValidEmail('invalid')).toBe(false)
    expect(isValidEmail('@example.com')).toBe(false)
  })

  it('validatesStrongPassword', () => {
    expect(isStrongPassword('MyP@ss123')).toBe(true)
    expect(isStrongPassword('weak')).toBe(false)
    expect(isStrongPassword('NoNumbers!')).toBe(false)
  })

  it('validatesAge', () => {
    expect(isValidAge(25)).toBe(true)
    expect(isValidAge(17)).toBe(false)
    expect(isValidAge(121)).toBe(false)
  })
})

實作 Validator 模組 ⚡

現在讓我們實作對應的驗證函數,這是一個非常實用的練習:

建立 src/validator.ts

export function isValidEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  return emailRegex.test(email)
}

export function isStrongPassword(password: string): boolean {
  if (password.length < 8) return false
  
  const hasUpperCase = /[A-Z]/.test(password)
  const hasLowerCase = /[a-z]/.test(password)
  const hasNumbers = /\d/.test(password)
  const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password)
  
  return hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar
}

export function isValidAge(age: number): boolean {
  return age >= 18 && age <= 120
}

執行測試 🚦

執行所有 Day 02 的測試:

npm test tests/day02

你應該會看到所有測試通過,並且能清楚了解每個斷言的作用。

小挑戰 🎮

試試看完成這些挑戰:

  1. 基礎挑戰:為 isValidEmail 函數增加更嚴格的驗證規則
  2. 進階挑戰:實作一個 isValidPhoneNumber 函數並寫測試
  3. 思考題:什麼時候該用 toBe() 而不是 toEqual()

今天學到什麼? 📚

1. 斷言的核心概念

  • 斷言是測試的心臟,負責驗證期望結果
  • 好的斷言讓測試意圖清晰易懂
  • 斷言失敗時提供有價值的調試資訊

2. Vitest 斷言方法

  • toBe(): 嚴格相等比較
  • toEqual(): 深度內容比較
  • toBeTruthy() / toBeFalsy(): 真假值檢查
  • toBeGreaterThan() / toBeLessThan(): 數值比較
  • toContain() / toMatch(): 字串檢查

3. 斷言選擇策略

  • 選擇最具表達力的斷言方法
  • 使用描述性的變數名稱
  • 複雜邏輯分步驟驗證

4. 實際應用場景

  • 表單驗證邏輯
  • 資料格式化功能
  • 業務規則檢查

總結

今天我們深入學習了斷言的使用,從基本的相等比較到複雜的字串匹配、數值比較、陣列物件檢查。透過實作驗證器函數,我們練習了:

  • 選擇合適的斷言方法讓測試更具表達力
  • 編寫清晰的測試案例名稱
  • 分步驟驗證複雜邏輯
  • 理解測試失敗訊息的重要性

斷言是測試的基石,掌握好斷言的使用,你的測試就會更加準確和有說服力。記住:好的測試不只是驗證功能正確,更是幫助我們快速定位問題的利器。

明天我們將進入 TDD 的核心 —「紅綠重構循環」! 💪


上一篇
Day 01 - 環境設置與第一個測試 🚀
下一篇
Day 03 - TDD 紅綠重構循環
系列文
React TDD 實戰:用 Vitest 打造可靠的前端應用28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言